﻿#region

using System;
using System.Diagnostics;

#endregion

namespace SharpObservation
{
    /// <summary>
    ///   Optimized version of System.ComponentModel.EventHandlerList
    /// </summary>
    /// <remarks>
    ///   .NET's EventHandler list has some characteristics that make it somewhat undesirable for general (non component) use:
    ///   1) It has dependencies on the Component class
    ///   2) It is a class, but should ideally be implemented as a struct.
    ///   3) It implements IDisposable, which is unnescessary.
    /// 
    ///   Implementing this list as a value type offers performance gains when instansiating the containing type, since 
    ///   no seperate memory allocation operation is required - the EventHandlerList is allocated as part of it's containing class.
    ///   EventHandlerList should always be implemented as a private field on a class that wishes to expose events, and it should 
    ///   never be returned to calling code or exposed outside of the type which uses it.
    /// </remarks>
    internal struct EventHandlerList
    {
        /// <summary>
        ///   Tracks the count of currently attached events.
        /// </summary>
        private int _attachedEventCount;

        /// <summary>
        ///   The head of the linked list of delegates
        /// </summary>
        private ListEntry _head;

        /// <summary>
        ///   Gets whether any delegates are currently attached to any events for this instance.
        /// </summary>
        /// <remarks>
        ///   Results will only be valid for events which were correctly registered using the RegisterEvent method
        /// </remarks>
        public bool IsAnyEventAttached
        {
            get { return _attachedEventCount > 0; }
        }

        /// <summary>
        ///   Gets the delegate associated with the given key
        /// </summary>
        /// <param name = "key">The key of the delegate sought</param>
        /// <returns>A delegate</returns>
        public Delegate this[object key]
        {
            get
            {
                var entry = Find(key);
                return entry != null ? entry.Handler : null;
            }
        }

        /// <summary>
        ///   Adds a propertyChanging to the given event
        /// </summary>
        /// <param name = "key">The key used to identify the event</param>
        /// <param name = "value">The delegate to invoke when the event is fired</param>
        [DebuggerStepThrough]
        public void AddHandler(object key, Delegate value)
        {
            var entry = Find(key);
            if (entry != null)
            {
                if (entry.Handler == null)
                    _attachedEventCount++;

                entry.Handler = Delegate.Combine(entry.Handler, value);
            }
            else
            {
                _head = new ListEntry(key, value, _head);
                _attachedEventCount++;
            }
        }

        /// <summary>
        ///   Removes a propertyChanging from the given event
        /// </summary>
        /// <param name = "key">The key used to identify the event</param>
        /// <param name = "value">The delegate to remove from the firing chain</param>
        [DebuggerStepThrough]
        public void RemoveHandler(object key, Delegate value)
        {
            var entry = Find(key);
            if (entry == null) return;
            entry.Handler = Delegate.Remove(entry.Handler, value);
            if(entry.Handler == null)
                _attachedEventCount--;
        }

        /// <summary>
        /// Finds the specified key.
        /// </summary>
        /// <param name="key">The key.</param>
        /// <returns></returns>
        [DebuggerStepThrough]
        private ListEntry Find(object key)
        {
            for (var head = _head; head != null; head = head.Next)
                if (head.Key == key)
                    return head;

            return null;
        }


        #region Nested type: ListEntry

        private sealed class ListEntry
        {
            internal readonly object Key;
            internal readonly ListEntry Next;
            internal Delegate Handler;

            public ListEntry(object key, Delegate handler, ListEntry next)
            {
                Next = next;
                Key = key;
                Handler = handler;
            }
        }

        #endregion
    }
}